<!DOCTYPE html>
<html lang="en">
<head>
<title>Oimo.js Ragdoll</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=11" />
<link rel="shortcut icon" href="./assets/img/favicon.ico">
<link rel="stylesheet" href="css/demo.css">

<script src="js/demo.js"></script>
<script src="js/libs/three.min.js"></script>
<script src="js/controls/OrbitControls.js"></script>
<script src="../build/oimo.js"></script>

</head>
<body>
<canvas id="canvas"></canvas>
<div id='interface'>
    <input type="button" value="demo 1" onClick=populate(1)>
    <input type="button" value="demo 2" onClick=populate(2)>
    <input type="number" name="quantity" min="10" max="2000" value="32"  id='MaxNumber'>
    <input type="submit" onClick=populate()>
    <input type="button" value="collision on" onClick=switchCollision() id="col">
    <input type="number" name="gravity" min="-20" max="20" value="-4" id='gravity' onChange=gravity() >
</div>
<div id='info'></div>
<img style="position: absolute; top: 0; right: 0; border: 0;" src="assets/img/ribbon.png"></a>
<a id="ribbon" href="https://github.com/lo-th/Oimo.js"></a>  
<script>

    demolink();

    var isMobile = false;
    var antialias = true;

    // three var
    var camera, scene, light, renderer, canvas, controls;
    var meshs = [];
    var grounds = [];
    var geoBox, geoSphere, geoSphere2, geoCylinder, geoCylinder2;
    var matBox, matSphere, matBoxSleep, matSphereSleep, matGround,  matBoxSleep2, matBox2, matHead;
    var ToRad = 0.0174532925199432957;

    //oimo var
    var world = null;
    var bodys = null;
    var joints = null;
    var infos;
    var type = 1;
    var collision = true;
    var bgColor = 0x252627;

    init();
    loop();

    function init() {

        var n = navigator.userAgent;
        if (n.match(/Android/i) || n.match(/webOS/i) || n.match(/iPhone/i) || n.match(/iPad/i) || n.match(/iPod/i) || n.match(/BlackBerry/i) || n.match(/Windows Phone/i)){ isMobile = true;  antialias = false; }

        infos = document.getElementById("info");

        canvas = document.getElementById("canvas");

        camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
        camera.position.set( 0, 160, 300 );

        controls = new THREE.OrbitControls( camera, canvas );
        controls.target.set(0, 40, 0);
        controls.update();
        
        scene = new THREE.Scene();

        scene.add( new THREE.AmbientLight( 0x3D4143 ) );

        renderer = new THREE.WebGLRenderer({ canvas:canvas, precision: "mediump", antialias:antialias });
        renderer.setSize( window.innerWidth, window.innerHeight );
        

        var materialType = 'MeshBasicMaterial';

        if(!isMobile){
            light = new THREE.DirectionalLight( 0xffffff , 1);
            light.position.set( 300, 1000, 500 );
            light.target.position.set( 0, 0, 0 );
            light.castShadow = true;
            var d = 300;
            light.shadow.camera = new THREE.OrthographicCamera( -d, d, d, -d,  500, 1600 );
            light.shadow.bias = 0.0001;
            light.shadow.mapSize.width = light.shadow.mapSize.height = 1024;
            scene.add( light );

            materialType = 'MeshPhongMaterial';

            renderer.shadowMap.enabled = true;
            renderer.shadowMap.type = THREE.PCFShadowMap;
        }

        // background
        var buffgeoBack = new THREE.BufferGeometry();
        buffgeoBack.fromGeometry( new THREE.IcosahedronGeometry(3000,2) );
        var back = new THREE.Mesh( buffgeoBack, new THREE.MeshBasicMaterial( { map:gradTexture([[0.75,0.6,0.4,0.25], ['#1B1D1E','#3D4143','#72797D', '#b0babf']]), side:THREE.BackSide, depthWrite: false, fog:false }  ));
        back.geometry.applyMatrix(new THREE.Matrix4().makeRotationZ(15*ToRad));
        scene.add( back );

        // geometry
        geoCylinder = new THREE.BufferGeometry();
        geoCylinder2 = new THREE.BufferGeometry();
        geoSphere = new THREE.BufferGeometry();
        geoSphere2 = new THREE.BufferGeometry();
        geoBox = new THREE.BufferGeometry();

        geoCylinder.fromGeometry( new THREE.CylinderGeometry( 0.5, 0.5, 1, 16 ) );
        geoCylinder2.fromGeometry( new THREE.CylinderGeometry( 0.5, 0.5, 1, 16 ) );
        geoSphere.fromGeometry( new THREE.SphereGeometry( 1 , 20, 10 ) );
        geoSphere2.fromGeometry(new THREE.SphereGeometry( 0.5 , 10, 6 ) );
        geoBox.fromGeometry( new THREE.BoxGeometry( 1, 1, 1 ) );

        geoCylinder2.applyMatrix( new THREE.Matrix4().makeRotationZ( Math.PI / 2 ) );

        // material
        matSphere      = new THREE[materialType]( { map: basicTexture(0), name:'sph',transparent:true, opacity:0.6, shininess:120, specular:0xffffff} );
        matHead        = new THREE[materialType]( { color: 0xe8b36d, name:'sphHH', shininess:60, specular:0xffffff  } );
        matBox         = new THREE[materialType]( { map: basicTexture(2), name:'box', shininess:100, specular:0xffffff  } );
        matBox2        = new THREE[materialType]( { map: basicTexture(2,1), name:'box2', shininess:100, specular:0xffffff  } );
        matBox3        = new THREE[materialType]( { map: basicTexture(2,0), name:'box3', shininess:100, specular:0xffffff  } );
        matSphereSleep = new THREE[materialType]( { map: basicTexture(1), name:'ssph',transparent:true, opacity:0.8 } );
        matBoxSleep    = new THREE[materialType]( { map: basicTexture(3), name:'sbox' } );
        matBoxSleep2   = new THREE[materialType]( { map: basicTexture(3,1), name:'sbox2' } );
        matBoxSleep3   = new THREE[materialType]( { map: basicTexture(3,0), name:'sbox3' } );
        matGround      = new THREE[materialType]( { color: 0x3D4143, transparent:true, opacity:0.5, shininess: 10 } );

        // events

        window.addEventListener( 'resize', onWindowResize, false );

        // physics

        initOimoPhysics();

    }

    function loop() {

        requestAnimationFrame( loop );
        
        updateOimoPhysics();
        renderer.render( scene, camera );
        
    }

    function onWindowResize() {

        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize( window.innerWidth, window.innerHeight );

    }

    function addStaticBox(size, position, rotation) {
        var mesh = new THREE.Mesh( new THREE.BoxGeometry( size[0], size[1], size[2] ), matGround );
        mesh.position.set( position[0], position[1], position[2] );
        mesh.rotation.set( rotation[0]*ToRad, rotation[1]*ToRad, rotation[2]*ToRad );
        scene.add( mesh );
        grounds.push(mesh);
        mesh.castShadow = true;
        mesh.receiveShadow = true;
    }

    function addThreeMesh(size, position, rotation, color, type) {
        var mesh, mat, m2;
        
        if(color===1){ mat = matSphere; }
        else if(color===2){mat = matBox2;}
        else if(color===3){mat = matBox3;}
        else{ mat = matBox;}

        if(type==='sphere'){
            mesh = new THREE.Mesh( geoSphere, mat );
            m2 = new THREE.Mesh( geoSphere2, matHead );
            m2.scale.set(1,1.4,1);
            m2.position.set(0,-0.3,0);
            mesh.add(m2);
        }
        else if(type==='cylinder'){mesh = new THREE.Mesh( geoCylinder, mat );}
        else if(type==='cylinder2'){mesh = new THREE.Mesh( geoCylinder2, mat );}
        else { mesh = new THREE.Mesh( geoBox, mat ); }
        mesh.scale.set( size[0], size[1], size[2] );
        if(position)mesh.position.set( position[0], position[1], position[2] );
        if(rotation)mesh.rotation.set( rotation[0]*ToRad, rotation[1]*ToRad, rotation[2]*ToRad );
        scene.add( mesh );
        mesh.castShadow = true;
        mesh.receiveShadow = true;
        return mesh;
    }

    function clearMesh(){
        var i=meshs.length;
        while (i--) scene.remove(meshs[ i ]);
        i = grounds.length;
        while (i--) scene.remove(grounds[ i ]);
        grounds = [];
        meshs = [];
    }

    //----------------------------------
    //  OIMO PHYSICS
    //----------------------------------

    function initOimoPhysics(){

        world = new OIMO.World({info:true, worldscale:100});
        gravity();
        populate(type);
        //setInterval(updateOimoPhysics, 1000/60);
    }

    function switchCollision() {
        var but = document.getElementById("col");
        if(collision){ collision = false; but.value = "collision off";}
        else{ collision = true; but.value = "collision on";}
        populate();
    }

    function populate(n) {
        var max = document.getElementById("MaxNumber").value;
        if(n===1) type = 1
        else if(n===2) type = 2;

        // reset old
        clearMesh();
        world.clear();
        bodys = [];
        joints = [];

        var ground;
        
        if(type===1){
            ground = world.add({size:[1000, 40, 1000], pos:[0,-20,0]});
            addStaticBox([1000, 40, 1000], [0,-20,0], [0,0,0]);
        }else{
            ground = world.add({size:[300, 40, 300], pos:[0,-20,0]});
            addStaticBox([300, 40, 300], [0,-20,0], [0,0,0]);
        }

        var i = max;
        var j = 0;
        var k = 0;
        var l = 0;
        var m = 0;
        var px,py,pz;
        var spring = [2, 0.3];
        

        while (i--){
            l++;

            if(type===1){
                px = -450+(l*100);
                py = 200;
                pz = -350+(m*100); 
                if(l>7){m++; l=0}
            }else {
                px = 0;
                py = 200 + (i*150);
                pz = 0;
            }

            bodys[j+0] = world.add({type:"box", size:[20,10,15], pos:[px,py-20,pz], move:true,  name:'pelvis'+j });
            meshs[j+0] = addThreeMesh([20,10,15], null, null, 0, 'cylinder');
       
            bodys[j+1] = world.add({type:"box", size:[20,10,15], pos:[px,py-10,pz], move:true,  name:'spine1_'+j });
            meshs[j+1] = addThreeMesh([20,10,15], null, null, 0, 'cylinder');
       
            bodys[j+2] = world.add({type:"box", size:[20,10,15], pos:[px,py,pz], move:true, name:'spine2_'+j, noSleep:true });
            meshs[j+2] = addThreeMesh([20,10,15], null, null, 0, 'cylinder');
       
            bodys[j+3] = world.add({type:"box", size:[20,10,15], pos:[px,py+10,pz], move:true,  name:'spine3_'+j });
            meshs[j+3] = addThreeMesh([20,10,15], null, null, 0, 'cylinder');
       
            bodys[j+4] = world.add({type:"sphere", size:[10,10,10], pos:[px,py+30,pz], move:true,  name:'head'+j });
            meshs[j+4] = addThreeMesh([10,10,10], null, null, 1, 'sphere');
       
            joints[k+0] = world.add({type:"joint", body1:'pelvis'+j, body2:'spine1_'+j, pos1:[0,5,0], pos2:[0,-5,0], min:2, max:20, collision:collision, spring:spring });
            joints[k+1] = world.add({type:"joint", body1:'spine1_'+j, body2:'spine2_'+j, pos1:[0,5,0], pos2:[0,-5,0], min:2, max:20, collision:collision, spring:spring });
            joints[k+2] = world.add({type:"joint", body1:'spine2_'+j, body2:'spine3_'+j, pos1:[0,5,0], pos2:[0,-5,0], min:2, max:20, collision:collision, spring:spring });
            joints[k+3] = world.add({type:"joint", body1:'spine3_'+j, body2:'head'+j,   pos1:[0,5,0], pos2:[0,-10,0], min:2, max:20, collision:collision, spring:spring });
      
            //arm
       
            bodys[j+5] = world.add({type:"box", size:[20,10,10], pos:[px-20,py+8,pz], rot:[0,0,20], move:true,  name:'L_arm'+j });
            meshs[j+5] = addThreeMesh([20,10,10], null, null, 3, 'cylinder2');
            bodys[j+6] = world.add({type:"box", size:[20,8,8], pos:[px-40,py,pz], rot:[0,0,20], move:true,  name:'LF_arm'+j });
            meshs[j+6] = addThreeMesh([20,8,8], null, null, 3, 'cylinder2');

            bodys[j+7] = world.add({type:"box", size:[20,10,10], pos:[px+20,py+8,pz], rot:[0,0,-20], move:true,  name:'R_arm'+j });
            meshs[j+7] = addThreeMesh([20,10,10], null, null, 3, 'cylinder2');
            bodys[j+8] = world.add({type:"box", size:[20,8,8], pos:[px+40,py,pz], rot:[0,0,-20], move:true,  name:'RF_arm'+j });
            meshs[j+8] = addThreeMesh([20,8,8], null, null, 3, 'cylinder2');

            joints[k+4] = world.add({type:"joint", body1:'spine3_'+j, body2:'L_arm'+j, pos1:[-10,0,0], pos2:[10,0,0], axe1:[0,1,1], axe2:[0,1,1], collision:collision});
            joints[k+5] = world.add({type:"joint", body1:'spine3_'+j, body2:'R_arm'+j, pos1:[10,0,0], pos2:[-10,0,0], axe1:[0,1,1], axe2:[0,1,1], collision:collision});

            joints[k+6] = world.add({type:"joint", body1:'L_arm'+j, body2:'LF_arm'+j, pos1:[-10,0,0], pos2:[10,0,0], axe1:[0,1,0], axe2:[0,1,0], collision:collision});
            joints[k+7] = world.add({type:"joint", body1:'R_arm'+j, body2:'RF_arm'+j, pos1:[10,0,0], pos2:[-10,0,0], axe1:[0,1,0], axe2:[0,1,0], collision:collision});

            // leg

            bodys[j+9] = world.add({type:"box", size:[10,20,10], pos:[px-6,py-40,pz], rot:[0,0,-20], move:true, name:'L_leg'+j });
            meshs[j+9] = addThreeMesh([10,20,10], null, null, 0, 'cylinder');
            bodys[j+10] = world.add({type:"box", size:[8,20,8], pos:[px-15,py-70,pz], rot:[0,0,-20], move:true, name:'LF_leg'+j });
            meshs[j+10] = addThreeMesh([8,20,8], null, null, 0, 'cylinder');

            bodys[j+11] = world.add({type:"box", size:[10,20,10], pos:[px+6,py-40,pz], rot:[0,0,20], move:true, name:'R_leg'+j });
            meshs[j+11] = addThreeMesh([10,20,10], null, null, 0, 'cylinder');
            bodys[j+12] = world.add({type:"box", size:[8,20,8], pos:[px+15,py-70,pz], rot:[0,0,20], move:true, name:'RF_leg'+j });
            meshs[j+12] = addThreeMesh([8,20,8], null, null, 0, 'cylinder');

            joints[k+8] = world.add({type:"joint", body1:'pelvis'+j, body2:'L_leg'+j, pos1:[-6,-5,0], pos2:[0,10,0], min:2, max:60, collision:collision }); 
            joints[k+9] = world.add({type:"joint", body1:'pelvis'+j, body2:'R_leg'+j, pos1:[6,-5,0], pos2:[0,10,0], min:2, max:60, collision:collision });

            joints[k+10] = world.add({type:"joint", body1:'L_leg'+j, body2:'LF_leg'+j, pos1:[0,-10,0], pos2:[0,10,0], axe1:[1,0,0], axe2:[1,0,0], min:2, max:60, collision:collision});
            joints[k+11] = world.add({type:"joint", body1:'R_leg'+j, body2:'RF_leg'+j, pos1:[0,-10,0], pos2:[0,10,0], axe1:[1,0,0], axe2:[1,0,0], min:2, max:60, collision:collision});
       

            j+=13;
            k+=12;
        }
    }

    function updateOimoPhysics() {

        if(world == null) return;

        world.step();

        var p, r, m, x, y, z, mesh, body, i = bodys.length;

        while (i--){
            body = bodys[i];
            mesh = meshs[i];

            if(!body.sleeping){

                mesh.position.copy(body.getPosition());
                mesh.quaternion.copy(body.getQuaternion());

                // change material
                if(mesh.material.name === 'sbox') mesh.material = matBox;
                if(mesh.material.name === 'sbox2') mesh.material = matBox2;
                if(mesh.material.name === 'sbox3') mesh.material = matBox3;
                if(mesh.material.name === 'ssph') mesh.material = matSphere; 

                // reset position
                if(mesh.position.y<-200 && bodys[i].name.substring(0,6)==='pelvis'){
                    x = -100 + Math.random()*200;
                    z = -100 + Math.random()*200;
                    y = 500 + Math.random()*200;
                    // chest
                    bodys[i+0].resetPosition(x,y-20,z);
                    bodys[i+1].resetPosition(x,y-10,z);
                    bodys[i+2].resetPosition(x,y,z);
                    bodys[i+3].resetPosition(x,y+10,z);
                    bodys[i+4].resetPosition(x,y+30,z);
                    // arm
                    bodys[i+5].resetPosition(x-20,y+8,z);
                    bodys[i+6].resetPosition(x-40,y,z);
                    bodys[i+7].resetPosition(x+20,y+8,z);
                    bodys[i+8].resetPosition(x+40,y,z);
                    // leg
                    bodys[i+9].resetPosition(x-6,y-40,z);
                    bodys[i+10].resetPosition(x-15,y-70,z);
                    bodys[i+11].resetPosition(x+6,y-40,z);
                    bodys[i+12].resetPosition(x+15,y-70,z);

                    bodys[i+0].resetRotation(0,0,0);
                    bodys[i+1].resetRotation(0,0,0);
                    bodys[i+2].resetRotation(0,0,0);
                    bodys[i+3].resetRotation(0,0,0);
                    bodys[i+4].resetRotation(0,0,0);

                    bodys[i+5].resetRotation(0,0,20);
                    bodys[i+6].resetRotation(0,0,20);
                    bodys[i+7].resetRotation(0,0,-20);
                    bodys[i+8].resetRotation(0,0,-20);

                    bodys[i+9].resetRotation(0,0,-20);
                    bodys[i+10].resetRotation(0,0,-20);
                    bodys[i+11].resetRotation(0,0,20);
                    bodys[i+12].resetRotation(0,0,20);
                }
            } else {
                if(mesh.material.name === 'box') mesh.material = matBoxSleep;
                if(mesh.material.name === 'box2') mesh.material = matBoxSleep2;
                if(mesh.material.name === 'box3') mesh.material = matBoxSleep3;
                if(mesh.material.name === 'sph') mesh.material = matSphereSleep;
            }
        }

        infos.innerHTML = world.getInfo();     
    }

    function gravity(){
        nG = document.getElementById("gravity").value;
        world.gravity = new OIMO.Vec3(0, nG, 0);
        if(bodys){
            var i = bodys.length;
            while (i--){bodys[i].body.awake();}
        }
    }

    //----------------------------------
    //  TEXTURES
    //----------------------------------

    function gradTexture(color) {
        var c = document.createElement("canvas");
        var ct = c.getContext("2d");
        var size = 1024;
        c.width = 16; c.height = size;
        var gradient = ct.createLinearGradient(0,0,0,size);
        var i = color[0].length;
        while(i--){ gradient.addColorStop(color[0][i],color[1][i]); }
        ct.fillStyle = gradient;
        ct.fillRect(0,0,16,size);
        var texture = new THREE.Texture(c);
        texture.needsUpdate = true;
        return texture;
    }

    function basicTexture(n, p){
        var canvas = document.createElement( 'canvas' );
        canvas.width = canvas.height = 64;
        var ctx = canvas.getContext( '2d' );
        var colors = [];
        var grd;
        if(n===0){ // sphere
            colors[0] = "#C8CAC0";
            colors[1] = "#989A90";
        }
        if(n===1){ // sphere sleep
            colors[0] = "#989A90";
            colors[1] = "#585858";
        }
        if(n===2){ // box
            colors[0] = "#AA8058";
            colors[1] = "#FFAA58";
        }
        if(n===3){ // box sleep
            colors[0] = "#383838";
            colors[1] = "#585858";
        }

        if(p) grd=ctx.createLinearGradient(0,0,64,0);
        else grd=ctx.createLinearGradient(0,0,0,64);
        grd.addColorStop(0,colors[1]);
        grd.addColorStop(1,colors[0]);
  
       ctx.fillStyle = grd;
       ctx.fillRect(0, 0, 64, 64);

       var tx = new THREE.Texture(canvas);
       tx.needsUpdate = true;
       return tx;
    }

</script>
</body>
</html>
